package pl.vm.domain;

import java.util.Iterator;
import java.util.NoSuchElementException;

import pl.vm.domain.exceptions.InvalidPlayerException;
import pl.vm.domain.exceptions.NotEnoughSpaceException;
import pl.vm.domain.exceptions.WrongPlayerPositionException;

public class SquadComposition implements Iterable<Player> {

	private Player opposite;
	private Player setter;
	private Player receiver1;
	private Player receiver2;
	private Player blocker1;
	private Player blocker2;
	private Player libero;

	public SquadComposition() {}
	
	public SquadComposition(Player opposite, Player setter, Player receiver1, Player receiver2,
			Player blocker1, Player blocker2, Player libero) throws WrongPlayerPositionException {
		super();
		this.opposite = assertOpposite(opposite);
		this.setter = assertSetter(setter);
		this.receiver1 = assertReceiver(receiver1);
		this.receiver2 = assertReceiver(receiver2);
		this.blocker1 = assertBlocker(blocker1);
		this.blocker2 = assertBlocker(blocker2);
		this.libero = assertLibero(libero);
	}

	public boolean validate() {
		try {
			assertOpposite(opposite);
			assertSetter(setter);
			assertReceiver(receiver1);
			assertReceiver(receiver2);
			assertBlocker(blocker1);
			assertBlocker(blocker2);
			assertLibero(libero);
		} catch (WrongPlayerPositionException e) {
			return false;
		} catch (NullPointerException npe) {
			return false;
		}
		return true;
	}
	
	public boolean exist(Player player) {
		switch (player.getPosition()) {
		case BLOCKER:
			return player.equals(blocker1) || player.equals(blocker2);
		case COACH:
			return false;
		case LIBERO:
			return player.equals(libero);
		case OPPOSITE:
			return player.equals(opposite);
		case RECEIVER:
			return player.equals(receiver1) || player.equals(receiver2);
		case SETTER :
			return player.equals(setter);
		}
		return false;
	}
	
	private Player assertOpposite(Player opposite) throws WrongPlayerPositionException {
		if (opposite.getPosition() == PlayerPosition.OPPOSITE) {
			return opposite;
		} else {
			throw new WrongPlayerPositionException("Player: " + opposite + " is not"
					+ PlayerPosition.OPPOSITE);
		}
	}

	private Player assertSetter(Player setter) throws WrongPlayerPositionException {
		if (setter.getPosition() == PlayerPosition.SETTER) {
			return setter;
		} else {
			throw new WrongPlayerPositionException("Player: " + setter + " is not"
					+ PlayerPosition.SETTER);
		}
	}

	private Player assertReceiver(Player receiver) throws WrongPlayerPositionException {
		if (receiver.getPosition() == PlayerPosition.RECEIVER) {
			return receiver;
		} else {
			throw new WrongPlayerPositionException("Player: " + receiver + " is not"
					+ PlayerPosition.RECEIVER);
		}
	}

	private Player assertBlocker(Player blocker) throws WrongPlayerPositionException {
		if (blocker.getPosition() == PlayerPosition.BLOCKER) {
			return blocker;
		} else {
			throw new WrongPlayerPositionException("Player: " + blocker + " is not"
					+ PlayerPosition.BLOCKER);
		}
	}

	private Player assertLibero(Player libero) throws WrongPlayerPositionException {
		if (libero.getPosition() == PlayerPosition.LIBERO) {
			return libero;
		} else {
			throw new WrongPlayerPositionException("Player: " + libero + " is not"
					+ PlayerPosition.LIBERO);
		}
	}

	public Player getOpposite() {
		return opposite;
	}

	public Player getSetter() {
		return setter;
	}

	public Player getReceiver1() {
		return receiver1;
	}

	public Player getReceiver2() {
		return receiver2;
	}

	public Player getBlocker1() {
		return blocker1;
	}

	public Player getBlocker2() {
		return blocker2;
	}

	public Player getLibero() {
		return libero;
	}

	public void setOpposite(Player opposite) throws WrongPlayerPositionException {
		this.opposite = assertOpposite(opposite);
	}

	public void setSetter(Player setter) throws WrongPlayerPositionException {
		this.setter = assertSetter(setter);
	}

	public void setReceiver1(Player receiver1) throws WrongPlayerPositionException {
		this.receiver1 = assertReceiver(receiver1);
	}

	public void setReceiver2(Player receiver2) throws WrongPlayerPositionException {
		this.receiver2 = assertReceiver(receiver2);
	}

	public void setBlocker1(Player blocker1) throws WrongPlayerPositionException {
		this.blocker1 = assertBlocker(blocker1);
	}

	public void setBlocker2(Player blocker2) throws WrongPlayerPositionException {
		this.blocker2 = assertBlocker(blocker2);
	}

	public void setLibero(Player libero) throws WrongPlayerPositionException {
		this.libero = assertLibero(libero);
	}

	public void removePlayer(Player player) throws InvalidPlayerException {
		switch (player.getPosition()) {
		case BLOCKER:
			if (player.equals(this.blocker1)) {
				this.blocker1 = null;
			} else if (player.equals(this.blocker2)) {
				this.blocker2 = null;
			} 
			break;
		case COACH:
			throw new InvalidPlayerException();
		case LIBERO:
			if (player.equals(libero)) {
				this.libero = null;
			} 
			break;
		case OPPOSITE:
			if (player.equals(opposite)) {
				this.opposite = null;
			} 
			break;
		case RECEIVER:
			if (player.equals(receiver1)) {
				this.receiver1 = null;
			} else if (player.equals(receiver2)) {
				this.receiver2 = null;
			} 
			break;
		case SETTER:
			if (player.equals(setter)) {
				this.setter = null;
			} 
			break;
		}
	}
	
	public void addPlayer(Player player) throws NotEnoughSpaceException, InvalidPlayerException {
		switch (player.getPosition()) {
		case BLOCKER:
			if (this.blocker1 == null) {
				this.blocker1 = player;
			} else if (this.blocker2 == null) {
				this.blocker2 = player;
			} else {
				throw new NotEnoughSpaceException("Blockers already set.");
			}
			break;
		case COACH:
			throw new InvalidPlayerException("Coach cannot be added to squad.");
		case LIBERO:
			if (this.libero == null) {
				this.libero = player;
			} else {
				throw new NotEnoughSpaceException("Libero is already set.");
			}
			break;
		case OPPOSITE:
			if (this.opposite == null) {
				this.opposite = player;
			} else {
				throw new NotEnoughSpaceException("Opposite is already set.");
			}
			break;
		case RECEIVER:
			if (this.receiver1 == null) {
				this.receiver1 = player;
			} else if (this.receiver2 == null) {
				this.receiver2 = player;
			} else {
				throw new NotEnoughSpaceException("Receiver is already set.");
			}
			break;
		case SETTER:
			if (this.setter == null) {
				this.setter = player;
			} else {
				throw new NotEnoughSpaceException("Setter is already set.");				
			}
			break;
		}
	}

	@Override
	public Iterator<Player> iterator() {
		return new Iterator<Player>() {
			
			private Player current = opposite;
			
			@Override
			public void remove() {
				throw new UnsupportedOperationException();
			}
			
			@Override
			public Player next() {
				if (current == null) {
					throw new NoSuchElementException();
				}
				Player element = current;
				if (current == opposite) {
					current = setter;
				} else if (current == setter) {
					current = receiver1;
				} else if (current == receiver1) {
					current = receiver2;					
				} else if (current == receiver2) {
					current = blocker1;
				} else if (current == blocker1) {
					current = blocker2;
				} else if (current == blocker2) {
					current = libero;
				} else {
					current = null;
				}
				return element;
			}
			
			@Override
			public boolean hasNext() {
				return current == null ? false : true; 
			}
		};
	}
}
